package com.cz.video.website.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cz.video.common.constant.CacheNames;
import com.cz.video.common.constant.ClientConstants;
import com.cz.video.common.constant.CommonConstants;
import com.cz.video.common.core.domain.PageQuery;
import com.cz.video.common.core.page.TableDataInfo;
import com.cz.video.common.enums.UserType;
import com.cz.video.common.exception.ServiceException;
import com.cz.video.common.helper.LoginHelper;
import com.cz.video.common.utils.MapstructUtils;
import com.cz.video.common.utils.redis.RedisUtils;
import com.cz.video.website.domain.WebsiteFollow;
import com.cz.video.website.domain.bo.WebsiteFollowBo;
import com.cz.video.website.domain.vo.WebsiteFollowVo;
import com.cz.video.website.mapper.WebsiteFollowMapper;
import com.cz.video.website.service.WebsiteFollowService;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;

/**
 * <p>
 * 点赞 服务实现类
 * </p>
 *
 */
@Service
@AllArgsConstructor
public class WebsiteFollowServiceImpl implements WebsiteFollowService {
  private final WebsiteFollowMapper followMapper;

  @Override
  public TableDataInfo<WebsiteFollowVo> page(WebsiteFollowBo follow, PageQuery pageQuery) {
    Page<WebsiteFollowVo> page = followMapper.selectPageList(pageQuery.build(), this.buildQueryWrapper(follow),follow);
    return TableDataInfo.build(page);
  }

  @Override
  public List<WebsiteFollowVo> list(WebsiteFollowBo followBo) {
    return followMapper.selectWebsiteFollowList(this.buildQueryWrapper(followBo));
  }


  @Override
  @Transactional
  public int add(WebsiteFollowBo follow) {
      if (follow.getType()==null)
          follow.setType(ClientConstants.FOLLOW_TYPE);
      boolean black = isBlack(LoginHelper.getUserId(), follow.getFollowingId());
      boolean isFollow = isFollow(LoginHelper.getUserId(), follow.getFollowingId());

      //类型一样抛异常
      if (isFollow&& ClientConstants.FOLLOW_TYPE==follow.getType())
          throw new ServiceException("您已经关注过该用户");
      else if (black&&ClientConstants.BLACK_FOLLOW_TYPE==follow.getType())
          throw new ServiceException("您已经拉黑过该用户");
      //如果不是关注并且不是拉黑
      else if (!(!black&&!isFollow)){
          //不一样就把之前的删掉
          followMapper.delete(Wrappers.<WebsiteFollow>query()
                  .eq("del_flag",CommonConstants.NOT_DELETED)
              .eq("follower_id",LoginHelper.getUserId())
              .eq("following_id", follow.getFollowingId()));
      }

      WebsiteFollow WebsiteFollow = MapstructUtils.convert(follow, WebsiteFollow.class);
    WebsiteFollow.setCreateBy(UserType.APP_USER.getUserType() +":"+LoginHelper.getUserId());
      int res = followMapper.insert(WebsiteFollow);
      //put到redis中
      if (res!=0){
          RedisUtils.getClient().getMap(CacheNames.FollowLIST + ":"+LoginHelper.getUserId())
              .put(String.valueOf(follow.getFollowingId()),follow.getType());
      }
      return res;
  }



  @Override
  public int deleteByUserIds(Long[] userIds) {
      if (ArrayUtil.isEmpty(userIds))
          return 0;
      int res = followMapper.delete(Wrappers.<WebsiteFollow>query()
          .eq("follower_id", LoginHelper.getUserId())
          .in("following_id", CollUtil.toList(userIds)));
      if (res!=0) {
          for (Long userId : userIds) {
              RedisUtils.getClient().getMap(CacheNames.FollowLIST + ":" + LoginHelper.getUserId())
                .fastRemove(String.valueOf(userId));
          }
      }
      return res;
  }



    @Override
    public boolean isBlack(Long userId, Long currentUserId) {
        String key = CacheNames.FollowLIST + ":" + userId;
        Integer followerType=RedisUtils.getCacheMapValue(key,String.valueOf(currentUserId));
        AtomicReference<Boolean> black= new AtomicReference<>(false);
      if (followerType==null){
          //如果不存在这个key，说明黑名单还没有加载到缓存了
          if (!RedisUtils.isExistsObject(key)){
              buildFollowCache(userId,followList-> black.set(followList.stream().anyMatch(
                  follow -> follow.getType() == ClientConstants.BLACK_FOLLOW_TYPE && follow.getFollowingId().equals(userId))));
          }
      }else if (ClientConstants.BLACK_FOLLOW_TYPE==followerType)
          black.set(true);
      return black.get();
    }

    @Override
    public boolean isFollow(Long userId, Long currentUserId) {
        String key = CacheNames.FollowLIST + ":" + userId;
        Integer followerType=RedisUtils.getCacheMapValue(key,String.valueOf(currentUserId));
        AtomicReference<Boolean> isFollow= new AtomicReference<>(false);
        if (followerType==null){
            //如果不存在这个key，说明黑名单还没有加载到缓存了
            if (!RedisUtils.isExistsObject(key)){
                buildFollowCache(userId,followList-> isFollow.set(followList.stream().anyMatch(
                    follow -> follow.getType() == ClientConstants.FOLLOW_TYPE && follow.getFollowingId().equals(userId))));
            }
        }else if (ClientConstants.FOLLOW_TYPE==followerType)
            isFollow.set(true);
        return isFollow.get();
    }

    private void buildFollowCache(Long userId, Consumer<List<WebsiteFollow>> consumer){
        String key = CacheNames.FollowLIST + ":" + userId;
        List<WebsiteFollow> followList = followMapper.selectList(Wrappers.<WebsiteFollow>lambdaQuery()
            .eq(WebsiteFollow::getDelFlag, CommonConstants.NOT_DELETED)
            .eq(WebsiteFollow::getFollowerId, userId));
        HashMap<String, Integer> map = new HashMap<>();
        //如果没有关注或拉黑任何人，就放一个占位符，防止每次都达到数据库
        if (CollUtil.isEmpty(followList))
            map.put("0",0);
        else {
            if (consumer!=null)
                consumer.accept(followList);
            followList.forEach(follow -> map.put(String.valueOf(follow.getFollowingId()), follow.getType()));
        }
        RedisUtils.getClient().getMap(key).putAll(map);
    }


    /**
   * 根据查询条件构建query wrapper
   *
   * @param follow 查询条件
   *
   * @return query wrapper
   */
  private Wrapper<WebsiteFollow> buildQueryWrapper(WebsiteFollowBo follow) {
      QueryWrapper<WebsiteFollow> wrapper = Wrappers.query();
    wrapper
        .eq("cf.del_flag", CommonConstants.NOT_DELETED)
        .eq(ObjectUtil.isNotNull(follow.getFollowerId()), "cf.follower_id", follow.getFollowerId())
        .eq(ObjectUtil.isNotNull(follow.getFollowingId()), "cf.following_id", follow.getFollowingId())
        .eq(ObjectUtil.isNotNull(follow.getType()),"cf.type",follow.getType())
        .orderByDesc("cf.create_time")
      ;
      return wrapper;
  }
}
